package org.protobee.util;

import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import org.jboss.netty.buffer.ChannelBuffer;


public class ByteUtils {
  /**
   * Returns the reverse of x.
   */
  public static byte[] reverse(final byte[] x) {
    int i, j;
    final int n;
    if ((n = x.length) > 0) {
      final byte[] ret = new byte[n];
      for (i = 0, j = n - 1; j >= 0;)
        ret[i++] = x[j--];
      return ret;
    }
    return x;
  }

  private static int readByte(InputStream is) throws IOException {
    int ret = is.read();
    if (ret == -1) throw new EOFException();
    return ret;
  }

  public static boolean isBitSet(short shortIn, int bitPos) {
    int bitValue = 1 << bitPos;
    return (shortIn & bitValue) != 0;
  }

  public static short setBit(short shortIn, int bitPos) {
    short mask = (short) (1 << bitPos);
    return (short) (shortIn | mask);
  }

  /**
   * Little-endian bytes to short.
   * 
   * @requires x.length - offset &gt;= 2
   * @effects returns the value of x[offset .. offset + 2] as a short, assuming x is interpreted as
   *          a signed little-endian number (i.e., x[offset] is LSB). If you want to interpret it as
   *          an unsigned number, call ubytes2int() on the result.
   */
  public static short leb2short(final byte[] x, final int offset) {
    return (short) ((x[offset] & 0xFF) | (x[offset + 1] << 8));
  }

  public static short leb2short(ChannelBuffer buffer) {
    byte a = buffer.readByte();
    byte b = buffer.readByte();
    return (short) ((a & 0xFF) | (b << 8));
  }

  /**
   * Big-endian bytes to short.
   * 
   * @requires x.length - offset &gt;= 2
   * @effects returns the value of x[offset .. offset + 2] as a short, assuming x is interpreted as
   *          a signed big-endian number (i.e., x[offset] is MSB). If you want to interpret it as an
   *          unsigned number, call ubytes2int() on the result.
   */
  public static short beb2short(final byte[] x, final int offset) {
    return (short) ((x[offset] << 8) | (x[offset + 1] & 0xFF));
  }

  /**
   * Little-endian bytes to short - stream version.
   */
  public static short leb2short(final InputStream is) throws IOException {
    return (short) ((readByte(is) & 0xFF) | (readByte(is) << 8));
  }

  /**
   * Big-endian bytes to short - stream version.
   */
  public static short beb2short(final InputStream is) throws IOException {
    return (short) ((readByte(is) << 8) | (readByte(is) & 0xFF));
  }

  /**
   * Little-endian bytes to int.
   * 
   * @requires x.length - offset &gt;= 4
   * @effects returns the value of x[offset .. offset + 4] as an int, assuming x is interpreted as a
   *          signed little-endian number (i.e., x[offset] is LSB) If you want to interpret it as an
   *          unsigned number, call ubytes2long() on the result.
   */
  public static int leb2int(final byte[] x, final int offset) {
    return (x[offset] & 0xFF) | ((x[offset + 1] & 0xFF) << 8) | ((x[offset + 2] & 0xFF) << 16)
        | (x[offset + 3] << 24);
  }

  public static int leb2int(ChannelBuffer buffer) {
    byte a = buffer.readByte();
    byte b = buffer.readByte();
    byte c = buffer.readByte();
    byte d = buffer.readByte();

    return (a & 0xFF) | ((b & 0xFF) << 8) | ((c & 0xFF) << 16) | (d << 24);
  }

  //
  // public static short leb2short(ChannelBuffer buffer) {
  // byte b = buffer.readByte();
  // return (short) ((b & 0xFF) | (b << 8));
  // }


  /**
   * Big-endian bytes to int.
   * 
   * @requires x.length - offset &gt;= 4
   * @effects returns the value of x[offset .. offset + 4] as an int, assuming x is interpreted as a
   *          signed big-endian number (i.e., x[offset] is MSB) If you want to interpret it as an
   *          unsigned number, call ubytes2long() on the result.
   */
  public static int beb2int(final byte[] x, final int offset) {
    return (x[offset] << 24) | ((x[offset + 1] & 0xFF) << 16) | ((x[offset + 2] & 0xFF) << 8)
        | (x[offset + 3] & 0xFF);
  }

  /**
   * Little-endian bytes to int - stream version.
   */
  public static int leb2int(final InputStream is) throws IOException {
    return (readByte(is) & 0xFF) | ((readByte(is) & 0xFF) << 8) | ((readByte(is) & 0xFF) << 16)
        | (readByte(is) << 24);
  }

  /**
   * Big-endian bytes to int - stream version.
   */
  public static int beb2int(final InputStream is) throws IOException {
    return (readByte(is) << 24) | ((readByte(is) & 0xFF) << 16) | ((readByte(is) & 0xFF) << 8)
        | (readByte(is) & 0xFF);
  }

  /**
   * Little-endian bytes to int. Unlike leb2int(x, offset), this version can read fewer than 4
   * bytes. If n &lt; 4, the returned value is never negative.
   * 
   * @param x the source of the bytes
   * @param offset the index to start reading bytes
   * @param n the number of bytes to read, which must be between 1 and 4, inclusive
   * @return the value of x[offset .. offset + N] as an int, assuming x is interpreted as an
   *         unsigned little-endian number (i.e., x[offset] is LSB).
   * @exception IllegalArgumentException if n is less than 1 or greater than 4
   * @exception IndexOutOfBoundsException if offset &lt; 0 or offset + n &gt; x.length
   */
  public static int leb2int(final byte[] x, final int offset, final int n)
      throws IndexOutOfBoundsException, IllegalArgumentException {
    switch (n) {
      case 1:
        return x[offset] & 0xFF;
      case 2:
        return (x[offset] & 0xFF) | ((x[offset + 1] & 0xFF) << 8);
      case 3:
        return (x[offset] & 0xFF) | ((x[offset + 1] & 0xFF) << 8) | ((x[offset + 2] & 0xFF) << 16);
      case 4:
        return (x[offset] & 0xFF) | ((x[offset + 1] & 0xFF) << 8) | ((x[offset + 2] & 0xFF) << 16)
            | (x[offset + 3] << 24);
      default:
        throw new IllegalArgumentException("No bytes specified");
    }
  }

  /**
   * Little-endian bytes to long. This version can read fewer than 8 bytes. If n &lt; 8, the
   * returned value is never negative.
   * 
   * @param x the source of the bytes
   * @param offset the index to start reading bytes
   * @param n the number of bytes to read, which must be between 1 and 8, inclusive
   * @return the value of x[offset .. offset + N] as an int, assuming x is interpreted as an
   *         unsigned little-endian number (i.e., x[offset] is LSB).
   * @exception IllegalArgumentException if n is less than 1 or greater than 8
   * @exception IndexOutOfBoundsException if offset &lt; 0 or offset + n &gt; x.length
   */
  public static long leb2long(final byte[] x, final int offset, final int n)
      throws IndexOutOfBoundsException, IllegalArgumentException {
    switch (n) {
      case 1:
        return x[offset] & 0xFFL;
      case 2:
        return (x[offset] & 0xFFL) | ((x[offset + 1] & 0xFFL) << 8);
      case 3:
        return (x[offset] & 0xFFL) | ((x[offset + 1] & 0xFFL) << 8)
            | ((x[offset + 2] & 0xFFL) << 16);
      case 4:
        return (x[offset] & 0xFFL) | ((x[offset + 1] & 0xFFL) << 8)
            | ((x[offset + 2] & 0xFFL) << 16) | ((x[offset + 3] & 0xFFL) << 24);
      case 5:
        return (x[offset] & 0xFFL) | ((x[offset + 1] & 0xFFL) << 8)
            | ((x[offset + 2] & 0xFFL) << 16) | ((x[offset + 3] & 0xFFL) << 24)
            | ((x[offset + 4] & 0xFFL) << 32);
      case 6:
        return (x[offset] & 0xFFL) | ((x[offset + 1] & 0xFFL) << 8)
            | ((x[offset + 2] & 0xFFL) << 16) | ((x[offset + 3] & 0xFFL) << 24)
            | ((x[offset + 4] & 0xFFL) << 32) | ((x[offset + 5] & 0xFFL) << 40);
      case 7:
        return (x[offset] & 0xFFL) | ((x[offset + 1] & 0xFFL) << 8)
            | ((x[offset + 2] & 0xFFL) << 16) | ((x[offset + 3] & 0xFFL) << 24)
            | ((x[offset + 4] & 0xFFL) << 32) | ((x[offset + 5] & 0xFFL) << 40)
            | ((x[offset + 6] & 0xFFL) << 48);
      case 8:
        return (x[offset] & 0xFFL) | ((x[offset + 1] & 0xFFL) << 8)
            | ((x[offset + 2] & 0xFFL) << 16) | ((x[offset + 3] & 0xFFL) << 24)
            | ((x[offset + 4] & 0xFFL) << 32) | ((x[offset + 5] & 0xFFL) << 40)
            | ((x[offset + 6] & 0xFFL) << 48) | ((long) x[offset + 7] << 56);
      default:
        throw new IllegalArgumentException("No bytes specified");
    }
  }

  /**
   * Little-endian bytes to long. Stream version.
   */
  public static long leb2long(InputStream is) throws IOException {
    return (readByte(is) & 0xFFL) | ((readByte(is) & 0xFFL) << 8) | ((readByte(is) & 0xFFL) << 16)
        | ((readByte(is) & 0xFFL) << 24) | ((readByte(is) & 0xFFL) << 32)
        | ((readByte(is) & 0xFFL) << 40) | ((readByte(is) & 0xFFL) << 48) | (readByte(is) << 56);
  }

  /**
   * Big-endian bytes to long. Unlike beb2long(x, offset), this version can read fewer than 4 bytes.
   * If n &lt; 4, the returned value is never negative.
   * 
   * @param x the source of the bytes
   * @param offset the index to start reading bytes
   * @param n the number of bytes to read, which must be between 1 and 4, inclusive
   * @return the value of x[offset .. offset + N] as an int, assuming x is interpreted as an
   *         unsigned big-endian number (i.e., x[offset] is MSB).
   * @exception IllegalArgumentException if n is less than 1 or greater than 4
   * @exception IndexOutOfBoundsException if offset &lt; 0 or offset + n &gt; x.length
   */
  public static int beb2int(final byte[] x, final int offset, final int n)
      throws IndexOutOfBoundsException, IllegalArgumentException {
    switch (n) {
      case 1:
        return x[offset] & 0xFF;
      case 2:
        return ((x[offset] & 0xFF) << 8) | (x[offset + 1] & 0xFF);
      case 3:
        return ((x[offset] & 0xFF) << 16) | ((x[offset + 1] & 0xFF) << 8) | (x[offset + 2] & 0xFF);
      case 4:
        return (x[offset] << 24) | ((x[offset + 1] & 0xFF) << 16) | ((x[offset + 2] & 0xFF) << 8)
            | (x[offset + 3] & 0xFF);
      default:
        throw new IllegalArgumentException("No bytes specified");
    }
  }



  /**
   * Short to little-endian bytes: writes x to buf[offset .. ].
   */
  public static void short2leb(final short x, final byte[] buf, final int offset) {
    buf[offset] = (byte) x;
    buf[offset + 1] = (byte) (x >> 8);
  }

  /**
   * Short to little-endian bytes: writes x to buf[offset .. ].
   */
  public static void short2leb(final short x, ChannelBuffer buffer) {
    buffer.writeByte((byte) x);
    buffer.writeByte((byte) (x >> 8));
  }

  /**
   * Long to little-endian bytes: writes x to buf[offset .. ].
   */
  public static void long2leb(final long x, ChannelBuffer buffer) {
    buffer.writeByte((byte) x);
    buffer.writeByte((byte) (x >> 8));
    buffer.writeByte((byte) (x >> 16));
    buffer.writeByte((byte) (x >> 24));
  }

  /**
   * Short to big-endian bytes: writes x to buf[offset .. ].
   */
  public static void short2beb(final short x, final byte[] buf, final int offset) {
    buf[offset] = (byte) (x >> 8);
    buf[offset + 1] = (byte) x;
  }

  /**
   * Short to little-endian bytes: writes x to given stream.
   */
  public static void short2leb(final short x, final OutputStream os) throws IOException {
    os.write((byte) x);
    os.write((byte) (x >> 8));
  }

  /**
   * Short to big-endian bytes: writes x to given stream.
   */
  public static void short2beb(final short x, final OutputStream os) throws IOException {
    os.write((byte) (x >> 8));
    os.write((byte) x);
  }

  /**
   * Int to little-endian bytes: writes x to buf[offset ..].
   */
  public static void int2leb(final int x, final byte[] buf, final int offset) {
    buf[offset] = (byte) x;
    buf[offset + 1] = (byte) (x >> 8);
    buf[offset + 2] = (byte) (x >> 16);
    buf[offset + 3] = (byte) (x >> 24);
  }

  public static void int2leb(final int x, final ChannelBuffer buffer) {
    buffer.writeByte((byte) x);
    buffer.writeByte((byte) (x >> 8));
    buffer.writeByte((byte) (x >> 16));
    buffer.writeByte((byte) (x >> 24));
  }

  /**
   * Long to big-endian bytes: writes x to buf[offset ..].
   */
  public static void long2beb(final long x, final byte[] buf, final int offset) {
    buf[offset] = (byte) (x >> 56);
    buf[offset + 1] = (byte) (x >> 48);
    buf[offset + 2] = (byte) (x >> 40);
    buf[offset + 3] = (byte) (x >> 32);
    buf[offset + 4] = (byte) (x >> 24);
    buf[offset + 5] = (byte) (x >> 16);
    buf[offset + 6] = (byte) (x >> 8);
    buf[offset + 7] = (byte) x;
  }

  /**
   * Long to little-endian bytes: writes x to buf[offset ..].
   */
  public static void long2leb(final long x, final byte[] buf, final int offset) {
    buf[offset] = (byte) x;
    buf[offset + 1] = (byte) (x >> 8);
    buf[offset + 2] = (byte) (x >> 16);
    buf[offset + 3] = (byte) (x >> 24);
    buf[offset + 4] = (byte) (x >> 32);
    buf[offset + 5] = (byte) (x >> 40);
    buf[offset + 6] = (byte) (x >> 48);
    buf[offset + 7] = (byte) (x >> 56);
  }

  /**
   * @return a big-endian array of byteCount bytes matching the passed-in number: ie: 1L,4 becomes
   *         -> [0,0,0,1]
   * @param byteCount a number between 0 and 8, the size of the resulting array
   * @throws NegativeArraySizeException if byteCount < 0
   * @throws ArrayIndexOutOfBoundsException if byteCount > 8
   */
  public static byte[] long2bytes(long i, int byteCount) {
    byte[] b = new byte[8];
    b[7] = (byte) (i);
    i >>>= 8;
    b[6] = (byte) (i);
    i >>>= 8;
    b[5] = (byte) (i);
    i >>>= 8;
    b[4] = (byte) (i);
    i >>>= 8;
    b[3] = (byte) (i);
    i >>>= 8;
    b[2] = (byte) (i);
    i >>>= 8;
    b[1] = (byte) (i);
    i >>>= 8;
    b[0] = (byte) (i);

    // We have an 8 byte array. Copy the interesting bytes into our new
    // array of size 'byteCount'
    byte[] bytes = new byte[byteCount];
    System.arraycopy(b, 8 - byteCount, bytes, 0, byteCount);
    return bytes;
  }

  /**
   * Int to big-endian bytes: writes x to buf[offset ..].
   */
  public static void int2beb(final int x, final byte[] buf, final int offset) {
    buf[offset] = (byte) (x >> 24);
    buf[offset + 1] = (byte) (x >> 16);
    buf[offset + 2] = (byte) (x >> 8);
    buf[offset + 3] = (byte) x;
  }

  /**
   * Int to big-endian bytes: writing only the up to n bytes.
   * 
   * @requires x fits in n bytes, else the stored value will be incorrect. n may be larger than the
   *           value required to store x, in which case this will pad with 0.
   * 
   * @param x the little-endian int to convert
   * @param out the outputstream to write to.
   * @param n the number of bytes to write, which must be between 1 and 4, inclusive
   * @exception IllegalArgumentException if n is less than 1 or greater than 4
   */
  public static void int2beb(final int x, OutputStream out, final int n) throws IOException {
    switch (n) {
      case 1:
        out.write((byte) x);
        break;
      case 2:
        out.write((byte) (x >> 8));
        out.write((byte) x);
        break;
      case 3:
        out.write((byte) (x >> 16));
        out.write((byte) (x >> 8));
        out.write((byte) x);
        break;
      case 4:
        out.write((byte) (x >> 24));
        out.write((byte) (x >> 16));
        out.write((byte) (x >> 8));
        out.write((byte) x);
        break;
      default:
        throw new IllegalArgumentException("invalid n: " + n);
    }
  }

  public static void int2beb(final int x, ChannelBuffer channel, final int n) {
    switch (n) {
      case 1:
        channel.writeByte((byte) x);
        // out.write((byte) x);
        break;
      case 2:
        channel.writeByte((byte) (x >> 8));
        channel.writeByte(x);

        // out.write((byte) (x >> 8));
        // out.write((byte) x);
        break;
      case 3:
        channel.writeByte((byte) (x >> 16));
        channel.writeByte((byte) (x >> 8));
        channel.writeByte((byte) x);
        // out.write((byte) (x >> 16));
        // out.write((byte) (x >> 8));
        // out.write((byte) x);
        break;
      case 4:
        channel.writeByte((byte) (x >> 24));
        channel.writeByte((byte) (x >> 16));
        channel.writeByte((byte) (x >> 8));
        channel.writeByte((byte) x);

        // out.write((byte) (x >> 24));
        // out.write((byte) (x >> 16));
        // out.write((byte) (x >> 8));
        // out.write((byte) x);
        break;
      default:
        throw new IllegalArgumentException("invalid n: " + n);
    }
  }

  /**
   * Int to little-endian bytes: writes x to given stream.
   */
  public static void int2leb(final int x, final OutputStream os) throws IOException {
    os.write((byte) x);
    os.write((byte) (x >> 8));
    os.write((byte) (x >> 16));
    os.write((byte) (x >> 24));
  }



  /**
   * Int to big-endian bytes: writes x to given stream.
   */
  public static void int2beb(final int x, final OutputStream os) throws IOException {
    os.write((byte) (x >> 24));
    os.write((byte) (x >> 16));
    os.write((byte) (x >> 8));
    os.write((byte) x);
  }



  /**
   * Returns the minimum number of bytes needed to encode x in little-endian format, assuming x is
   * non-negative. Note that leb2int(int2leb(x)) == x.
   * 
   * @param x a non-negative integer
   * @exception IllegalArgumentException x is negative
   */
  public static byte[] int2minLeb(final int x) throws IllegalArgumentException {
    if (x <= 0xFFFF) {
      if (x <= 0xFF) {
        if (x < 0) throw new IllegalArgumentException();
        return new byte[] {(byte) x};
      }
      return new byte[] {(byte) x, (byte) (x >> 8)};
    }
    if (x <= 0xFFFFFF) return new byte[] {(byte) x, (byte) (x >> 8), (byte) (x >> 16)};
    return new byte[] {(byte) x, (byte) (x >> 8), (byte) (x >> 16), (byte) (x >> 24)};
  }

  /**
   * Returns the minimum number of bytes needed to encode x in little-endian format, assuming x is
   * non-negative.
   * 
   * @param x a non-negative integer
   * @exception IllegalArgumentException x is negative
   */
  public static byte[] long2minLeb(final long x) throws IllegalArgumentException {
    if (x <= 0xFFFFFFFFFFFFFFL) {
      if (x <= 0xFFFFFFFFFFFFL) {
        if (x <= 0xFFFFFFFFFFL) {
          if (x <= 0xFFFFFFFFL) {
            if (x <= 0xFFFFFFL) {
              if (x <= 0xFFFFL) {
                if (x <= 0xFFL) {
                  if (x < 0) throw new IllegalArgumentException();
                  return new byte[] {(byte) x};
                }
                return new byte[] {(byte) x, (byte) (x >> 8)};
              }
              return new byte[] {(byte) x, (byte) (x >> 8), (byte) (x >> 16)};
            }
            return new byte[] {(byte) x, (byte) (x >> 8), (byte) (x >> 16), (byte) (x >> 24)};
          }
          return new byte[] {(byte) x, (byte) (x >> 8), (byte) (x >> 16), (byte) (x >> 24),
              (byte) (x >> 32)};
        }
        return new byte[] {(byte) x, (byte) (x >> 8), (byte) (x >> 16), (byte) (x >> 24),
            (byte) (x >> 32), (byte) (x >> 40)};
      }
      return new byte[] {(byte) x, (byte) (x >> 8), (byte) (x >> 16), (byte) (x >> 24),
          (byte) (x >> 32), (byte) (x >> 40), (byte) (x >> 48)};
    }

    return new byte[] {(byte) x, (byte) (x >> 8), (byte) (x >> 16), (byte) (x >> 24),
        (byte) (x >> 32), (byte) (x >> 40), (byte) (x >> 48), (byte) (x >> 56)};
  }

  /**
   * Returns the minimum number of bytes needed to encode x in big-endian format, assuming x is
   * non-negative. Note that beb2int(int2beb(x)) == x.
   * 
   * @param x a non-negative integer
   * @exception IllegalArgumentException x is negative
   */
  public static byte[] int2minBeb(final int x) throws IllegalArgumentException {
    if (x <= 0xFFFF) {
      if (x <= 0xFF) {
        if (x < 0) throw new IllegalArgumentException();
        return new byte[] {(byte) x};
      }
      return new byte[] {(byte) (x >> 8), (byte) x};
    }
    if (x <= 0xFFFFFF) return new byte[] {(byte) (x >> 16), (byte) (x >> 8), (byte) x};
    return new byte[] {(byte) (x >> 24), (byte) (x >> 16), (byte) (x >> 8), (byte) x};
  }



  /**
   * Interprets the value of x as an unsigned byte, and returns it as integer. For example,
   * ubyte2int(0xFF) == 255, not -1.
   */
  public static int ubyte2int(final byte x) {
    return x & 0xFF;
  }

  /**
   * Interprets the value of x as an unsigned two-byte number.
   */
  public static int ushort2int(final short x) {
    return x & 0xFFFF;
  }

  /**
   * Interprets the value of x as an unsigned four-byte number.
   */
  public static long uint2long(final int x) {
    return x & 0xFFFFFFFFL;
  }

  /**
   * Returns the int value that is closest to l. That is, if l can fit into a 32-bit unsigned
   * number, returns (int)l. Otherwise, returns either Integer.MAX_VALUE or Integer.MIN_VALUE as
   * appropriate.
   */
  public static int long2int(final long l) {
    int m;
    if (l < (m = Integer.MAX_VALUE) && l > (m = Integer.MIN_VALUE)) return (int) l;
    return m;
  }


  /**
   * Big-endian bytes to long. Unlike beb2long(x, offset), this version can read fewer than 8 bytes.
   * If n &lt; 8, the returned value is never negative.
   * 
   * @param x the source of the bytes
   * @param offset the index to start reading bytes
   * @param n the number of bytes to read, which must be between 1 and 8, inclusive
   * @return the value of x[offset .. offset + N] as a long, assuming x is interpreted as an
   *         unsigned big-endian number (i.e., x[offset] is MSB).
   * @exception IllegalArgumentException if n is less than 1 or greater than 8
   * @exception IndexOutOfBoundsException if offset &lt; 0 or offset + n &gt; x.length
   */
  public static long beb2long(final byte[] x, final int offset, final int n)
      throws IndexOutOfBoundsException, IllegalArgumentException {
    switch (n) {
      case 1:
        return x[offset] & 0xFFL;
      case 2:
        return (x[offset + 1] & 0xFFL) | ((x[offset] & 0xFFL) << 8);
      case 3:
        return (x[offset + 2] & 0xFFL) | ((x[offset + 1] & 0xFFL) << 8)
            | ((x[offset] & 0xFFL) << 16);
      case 4:
        return (x[offset + 3] & 0xFFL) | ((x[offset + 2] & 0xFFL) << 8)
            | ((x[offset + 1] & 0xFFL) << 16) | ((x[offset] & 0xFFL) << 24);
      case 5:
        return (x[offset + 4] & 0xFFL) | ((x[offset + 3] & 0xFFL) << 8)
            | ((x[offset + 2] & 0xFFL) << 16) | ((x[offset + 1] & 0xFFL) << 24)
            | ((x[offset] & 0xFFL) << 32);
      case 6:
        return (x[offset + 5] & 0xFFL) | ((x[offset + 4] & 0xFFL) << 8)
            | ((x[offset + 3] & 0xFFL) << 16) | ((x[offset + 2] & 0xFFL) << 24)
            | ((x[offset + 1] & 0xFFL) << 32) | ((x[offset] & 0xFFL) << 40);
      case 7:
        return (x[offset + 6] & 0xFFL) | ((x[offset + 5] & 0xFFL) << 8)
            | ((x[offset + 4] & 0xFFL) << 16) | ((x[offset + 3] & 0xFFL) << 24)
            | ((x[offset + 2] & 0xFFL) << 32) | ((x[offset + 1] & 0xFFL) << 40)
            | ((x[offset] & 0xFFL) << 48);
      case 8:
        return (x[offset + 7] & 0xFFL) | ((x[offset + 6] & 0xFFL) << 8)
            | ((x[offset + 5] & 0xFFL) << 16) | ((x[offset + 4] & 0xFFL) << 24)
            | ((x[offset + 3] & 0xFFL) << 32) | ((x[offset + 2] & 0xFFL) << 40)
            | ((x[offset + 1] & 0xFFL) << 48) | ((x[offset] & 0xFFL) << 56);
      default:
        throw new IllegalArgumentException("No bytes specified");
    }
  }
  
  /**
   * Returns the logarithm to the based 2 of value.
   * @param num the number to determine the log2 of. This is expceted to be a
   * power of 2
   */
  public static byte calculateLog2( int value )
  {
      if      (value <             0x10000)
          if      (value <           0x100)
              if      (value <        0x10)
                  if      (value <     0x4)
                      if  (value <     0x2) return  0; else return  1;
                  else if (value <     0x8) return  2; else return  3;
              else if (value <        0x40)
                  if      (value <    0x20) return  4; else return  5;
              else if (value <        0x80) return  6; else return  7;
          else if (value <          0x1000)
              if      (value <       0x400)
                  if      (value <   0x200) return  8; else return  9;
              else if (value <       0x800) return 10; else return 11;
          else if (value <          0x4000)
              if       (value <     0x2000) return 12; else return 13;
          else if (value <          0x8000) return 14; else return 15;
      else if (value <           0x1000000)
          if      (value <        0x100000)
              if      (value <     0x40000)
                  if      (value < 0x20000) return 16; else return 17;
              else if (value <     0x80000) return 18; else return 19;
          else if (value <        0x400000)
              if      (value <    0x200000) return 20; else return 21;
          else if (value <        0x800000) return 22; else return 23;
      else if (value <          0x10000000)
          if      (value <       0x4000000)
              if      (value <   0x2000000) return 24; else return 25;
          else if (value <       0x8000000) return 26; else return 27;
      else if (value <          0x40000000)
          if      (value <      0x20000000) return 28; else return 29;
      else return 30;
  }
  
}
